//
//  KDDUpdateBuilder.m
//  MengLiao
//
//  Created by xiuxin on 2022/7/15.
//  Copyright © 2022 ZHBen. All rights reserved.
//
#import "KDDUpdateBuilder.h"

@interface KDDUpdate (KDDStorage)

- (instancetype)initWithTarget:(KDDTargetType)target updateType:(KDDUpdateType)updateType;

- (instancetype)initWithPosition:(KDDPosition *)position updateType:(KDDUpdateType)updateType;

- (instancetype)initWithSectionData:(KDDSectionData *)sectionData updateType:(KDDUpdateType)updateType;

- (void)addSectionData:(KDDSectionData *)sectionData;

- (BOOL)matchPosition:(KDDPosition *)position;

- (void)addPosition:(KDDPosition *)position;

@end

@interface KOSUpdate (KDDStorage)

- (instancetype)initWithTarget:(KDDTargetType)target updateType:(KDDUpdateType)updateType;

@end

@interface KDDUpdateBuilder()

@property (nonatomic) BOOL batchUpdating;

@property (strong,nonatomic) NSMutableArray<KDDUpdate *> * batchUpdateList;

@end

@implementation KDDUpdateBuilder

+ (KDDUpdate *)sectionUpdate:(KDDUpdateType)updateType indexSet:(NSIndexSet *)indexSet
{
    KDDUpdate * update = [[KDDUpdate alloc] initWithTarget:KDDTargetSection updateType:updateType];
    update.indexSet = [indexSet mutableCopy];
    return update;
}

+ (KDDUpdate *)sectionUpdate:(KDDUpdateType)updateType sectionDataList:(NSArray<KDDSectionData *> *)sectionDataList indexSet:(NSIndexSet *)indexSet
{
    sectionDataList = [KDDSectionData copySectionDataList:sectionDataList];
    __block KDDUpdate * update = nil;
    NSInteger index = 0;
    [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
        KDDSectionData * sectionData = sectionDataList[index];
        sectionData.index = idx;
        if (update == nil)
        {
            update = [[KDDUpdate alloc] initWithSectionData:sectionData updateType:updateType];
        }
        else
        {
            [update addSectionData:sectionData];
        }
    }];
    return update;
}

+ (NSArray<KDDUpdate *> *)modelUpdateList:(KDDUpdateType)updateType modelList:(NSArray *)modelList positions:(NSArray<KDDPosition *> *)positionList
{
    KOSUpdate * headerUpdate = [[KOSUpdate alloc] initWithTarget:KDDTargetHeader updateType:KDDUpdateModify];
    KOSUpdate * cellUpdate = [[KOSUpdate alloc] initWithTarget:KDDTargetCell updateType:updateType];
    NSMutableArray<KOSUpdate *> * updateList = [NSMutableArray array];
    positionList = [self sortPositionList:positionList modelList:modelList];
    if (updateType == KDDUpdateDelete)
    {
        positionList = [[positionList reverseObjectEnumerator] allObjects];
    }
    KDDUpdate * update = nil;
    for (KDDPosition * position in positionList)
    {
        KOSUpdate * osUpdate = position.viewType == KDDViewTypeCell ? cellUpdate : headerUpdate;
        if (update == nil || ![update matchPosition:position])
        {
            KDDUpdateType uType = updateType;
            if (uType == KDDUpdateInsert && (position.viewType == KDDViewTypeHeader || position.viewType == KDDViewTypeFooter))
            {
                uType = KDDUpdateModify;
            }
            update = [[KDDUpdate alloc] initWithPosition:position updateType:uType];
            [osUpdate.updateList addObject:update];
        }
        else
        {
            [update addPosition:position];
        }
        [osUpdate.positionList addObject:position];
    }
    if (headerUpdate.updateList.count > 0)
    {
        [updateList addObject:headerUpdate];
    }
    if (cellUpdate.updateList.count > 0)
    {
        [updateList addObject:cellUpdate];
    }
    return updateList;
}

+ (NSArray<KDDPosition *> *)sortPositionList:(NSArray<KDDPosition *> *)positionList modelList:(NSArray *)modelList
{
    if (modelList != nil)
    {
        for (NSInteger index = 0; index < positionList.count; index++)
        {
            positionList[index].model = modelList[index];
        }
    }
    return [KDDPosition sortedPositionList:positionList];
}

+ (void)configSectionIndex:(NSArray<KDDSectionData *> *)sectionDataList
{
    for (NSInteger index = 0; index < sectionDataList.count; index++)
    {
        KDDSectionData * sectionData = sectionDataList[index];
        sectionData.index = index;
    }
}

#pragma mark --- Batch Update
- (void)performBatchUpdates:(void (^)(void))updates animated:(BOOL)animated completion:(void (^)(BOOL finished))completion
{
    self.batchUpdating = YES;
    self.batchUpdateList = [NSMutableArray array];
    updates();
    self.batchUpdating = NO;
    NSArray<KDDUpdate *> * updateList = [NSArray arrayWithArray:self.batchUpdateList];
    self.batchUpdateList = nil;
    if ([self.updateReceiver respondsToSelector:@selector(didReceiveBatchUpdateList:animated:completion:)])
    {
        [self.updateReceiver didReceiveBatchUpdateList:updateList animated:animated completion:completion];
    }
    else
    {
        for (KDDUpdate * update in updateList)
        {
            [self didCreateUpdate:update];
        }
    }
}

#pragma mark --- Section Update
- (void)insertSections:(NSArray<KDDSectionData *> *)sectionDataList atIndexes:(NSIndexSet *)indexSet
{
    if (indexSet.count > 0)
    {
        KDDUpdate * update = [KDDUpdateBuilder sectionUpdate:KDDUpdateInsert sectionDataList:sectionDataList indexSet:indexSet];
        [self didCreateUpdate:update];
    }
}

- (void)deleteSectionsAtIndexes:(NSIndexSet *)indexSet
{
    if (indexSet.count > 0)
    {
        KDDUpdate * update = [KDDUpdateBuilder sectionUpdate:KDDUpdateDelete indexSet:indexSet];
        [self didCreateUpdate:update];
    }
}

- (void)modifySections:(NSArray<KDDSectionData *> *)sectionDataList atIndexes:(NSIndexSet *)indexSet
{
    if (indexSet.count > 0)
    {
        KDDUpdate * update = [KDDUpdateBuilder sectionUpdate:KDDUpdateModify sectionDataList:sectionDataList indexSet:indexSet];
        [self didCreateUpdate:update];
    }
}

- (void)reloadSectionsAtIndexes:(NSIndexSet *)indexSet
{
    if (indexSet.count > 0)
    {
        KDDUpdate * update = [KDDUpdateBuilder sectionUpdate:KDDUpdateReload indexSet:indexSet];
        [self didCreateUpdate:update];
    }
}

#pragma mark --- Item Update
- (void)insertModels:(NSArray *)modelList atPositions:(NSArray<KDDPosition *> *)positionList
{
    if (positionList.count > 0)
    {
        NSArray<KDDUpdate *> * updateList = [KDDUpdateBuilder modelUpdateList:KDDUpdateInsert modelList:modelList positions:positionList];
        for (KDDUpdate * update in updateList)
        {
            [self didCreateUpdate:update];
        }
    }
}

- (void)deleteModelsAtPositions:(NSArray<KDDPosition *> *)positionList
{
    if (positionList.count > 0)
    {
        NSArray<KDDUpdate *> * updateList = [KDDUpdateBuilder modelUpdateList:KDDUpdateDelete modelList:nil positions:positionList];
        for (KDDUpdate * update in updateList)
        {
            [self didCreateUpdate:update];
        }
    }
}

- (void)modifyModelsAtPositions:(NSArray<KDDPosition *> *)positionList withModels:(NSArray *)modelList
{
    if (positionList.count > 0)
    {
        NSArray<KDDUpdate *> * updateList = [KDDUpdateBuilder modelUpdateList:KDDUpdateModify modelList:modelList positions:positionList];
        for (KDDUpdate * update in updateList)
        {
            [self didCreateUpdate:update];
        }
    }
}

- (void)reloadModelsAtPositions:(NSArray<KDDPosition *> *)positionList
{
    if (positionList.count > 0)
    {
        NSArray<KDDUpdate *> * updateList = [KDDUpdateBuilder modelUpdateList:KDDUpdateReload modelList:nil positions:positionList];
        for (KDDUpdate * update in updateList)
        {
            [self didCreateUpdate:update];
        }
    }
}

#pragma mark --- List Update
- (void)modifyData:(NSArray<KDDSectionData *> *)sectionDataList
{
    NSMutableArray<KDDSectionData *> * sectionList = [KDDSectionData copySectionDataList:sectionDataList];
    [KDDUpdateBuilder configSectionIndex:sectionList];
    KDDUpdate * update = [[KDDUpdate alloc] initWithTarget:KDDTargetList updateType:KDDUpdateModify];
    update.modelList = sectionList;
    [self didCreateUpdate:update];
}

- (void)reloadData
{
    KDDUpdate * update = [[KDDUpdate alloc] initWithTarget:KDDTargetList updateType:KDDUpdateReload];
    [self didCreateUpdate:update];
}

#pragma mark --- Notify UpdateReceiver
- (void)didCreateUpdate:(KDDUpdate *)update
{
    if (self.batchUpdating)
    {
        [self.batchUpdateList addObject:update];
    }
    else
    {
        [self.updateReceiver didReceiveUpdate:update];
    }
}

@end
